package cipher.algorithm;

import java.util.ArrayList;
import java.util.Random;

public class A51 extends Algorithm {
	
	private Lfsr lfsr19, lfsr22, lfsr23;
	private int c1 = 8; //3
	private int c2 = 10; //4
	private int c3 = 10; //7
	
	public A51(){	}
	
	public A51(int init1, int init2, int init3) {
		setKEY_LENGTH(8);
		int retroac1, retroac2, retroac3;
		retroac1 = 466945;  //"1110010000000000001"
		retroac2 = 3145729; //"1100000000000000000001"
		retroac3 = 7340161; //"11100000000000010000001"
		//lfsr1 = new LFSR_1(init1, retroac1);
		//lfsr2 = new LFSR_1(init2, retroac2);
		//lfsr3 = new LFSR_1(init3, retroac3);
		lfsr19 = new Lfsr(Integer.toBinaryString(init1).length(), init1, retroac1);
		lfsr22 = new Lfsr(Integer.toBinaryString(init2).length(), init2, retroac2);
		lfsr23 = new Lfsr(Integer.toBinaryString(init3).length(), init3, retroac3);
	}
	
	public A51(String key, String bloc_ctr) {
		//System.out.println(key.length()+" & "+bloc_ctr.length());
		if(key.length() == 64 && bloc_ctr.length() == 22){
			int i, len = 64, readRegister;
			long readKey, K = stringtoInt64(key, 64);
			//long readKey, K = stringtoInt(key);
			int counter22 = stringtoInt32(bloc_ctr, 22);
			//int counter22 = (int) stringtoInt(bloc_ctr);
			int retroac1, retroac2, retroac3;
			retroac1 = 0b1110010000000000001;
			retroac2 = 0b1100000000000000000001;
			retroac3 = 0b11100000000000010000001;
			Algorithm.setKEY_LENGTH(64);
			
			lfsr19 = new Lfsr();
			lfsr22 = new Lfsr();
			lfsr23 = new Lfsr();
			
			lfsr19.setSize(19);
			lfsr22.setSize(22);
			lfsr23.setSize(23);
			
			//System.out.println(this.toString());
			
			//retroactions
			lfsr19.setRetroaction(retroac1);
			lfsr22.setRetroaction(retroac2);
			lfsr23.setRetroaction(retroac3);
			
			//System.out.println(this.toString());
			
			//key cycles
			for(i = len; i > 0; i--){
				readRegister = readBit(lfsr19.getRegistre(),0);
				readKey = readBit(K,len-i);
				//if(((readRegister = readBit(lfsr19.getRegistre(),0)) ^ (readKey = readBit(K,len-i))) == 0){
				if((readRegister^readKey) == 0){
					lfsr19.setRegistre(write0(lfsr19.getRegistre(),0));
				}
				else{
					lfsr19.setRegistre(write1(lfsr19.getRegistre(),0));
				}
				lfsr19.generateNextNumber(1);
				
				readRegister = readBit(lfsr22.getRegistre(),0);
				if((readRegister^readKey) == 0){
					lfsr22.setRegistre(write0(lfsr22.getRegistre(),0));
				}
				else	lfsr22.setRegistre(write1(lfsr22.getRegistre(),0));
				lfsr22.generateNextNumber(1);
				
				readRegister = readBit(lfsr23.getRegistre(),0);
				if((readRegister^readKey) == 0){
					lfsr23.setRegistre(write0(lfsr23.getRegistre(),0));
				}
				else	lfsr23.setRegistre(write1(lfsr23.getRegistre(),0));
				lfsr23.generateNextNumber(1);
				
				//System.out.println(this.toString());
			}
			
			//System.out.println(this.toString());
			
			//counter cycles
			len = 22;
			for(i = len; i > 0; i--){
				readRegister = readBit(lfsr19.getRegistre(),0);
				readKey = readBit(counter22,len-i);
				//if(((readRegister = readBit(lfsr19.getRegistre(),0)) ^ (readKey = readBit(counter22,len-i))) == 0){
				if((readRegister^readKey) == 0){
					lfsr19.setRegistre(write0(lfsr19.getRegistre(),0));
				}
				else	lfsr19.setRegistre(write1(lfsr19.getRegistre(),0));
				lfsr19.generateNextNumber(1);
				
				readRegister = readBit(lfsr22.getRegistre(),0);
				if((readRegister^readKey) == 0){
					lfsr22.setRegistre(write0(lfsr22.getRegistre(),0));
				}
				else	lfsr22.setRegistre(write1(lfsr22.getRegistre(),0));
				lfsr22.generateNextNumber(1);
				
				readRegister = readBit(lfsr23.getRegistre(),0);
				if((readRegister^readKey) == 0){
					lfsr23.setRegistre(write0(lfsr23.getRegistre(),0));
				}
				else	lfsr23.setRegistre(write1(lfsr23.getRegistre(),0));
				lfsr23.generateNextNumber(1);
			}
			
			//System.out.println(this.toString());
			
			//100 empty cycles
			for(i = 100; i > 0; i--){
				lfsr19.generateNextNumber(1);
				lfsr22.generateNextNumber(1);
				lfsr23.generateNextNumber(1);
			}
			
			//System.out.println(this.toString());
			
		}
		else	System.out.println("A5/1_INIT_ERR");
	}
	
	public ArrayList<String> generateNewKey(int keySize){
		ArrayList<String> key = new ArrayList<String>();
		String init_key = "", counter = "", tmp = "";
		int i, size = 86;
		float number;
		Random rand = new Random();
		rand.setSeed(System.currentTimeMillis());

			for(i = size; i > 0; i--){
				number = rand.nextFloat();
				if(number >= 0.5)	tmp += '1';
				else	tmp += '0';
			}
			init_key = tmp.substring(0, 64);
			counter = tmp.substring(64, 86);
			key.add(init_key);
			key.add(counter);
			//key.add(tmp);
			
		return key;
	}

	@Override
	public int generateNextNumber() {
		int b = 0;
		int tmp = 0;
		//int a1 = readBit(((LFSR_1) lfsr1).getRegistre(), c1);
		//int a2 = readBit(((LFSR_1) lfsr2).getRegistre(), c2);
		//int a3 = readBit(((LFSR_1) lfsr3).getRegistre(), c3);
		int a1 = readBit(((Lfsr) lfsr19).getRegistre(), c1);
		int a2 = readBit(((Lfsr) lfsr22).getRegistre(), c2);
		int a3 = readBit(((Lfsr) lfsr23).getRegistre(), c3);

		int b1 = 0;
		int b2 = 0;
		int b3 = 0;
		
		//System.out.println(Integer.toBinaryString(a1)+" "+Integer.toBinaryString(a2)+" "+Integer.toBinaryString(a3));
		//System.out.println(Integer.toBinaryString(b1)+" "+Integer.toBinaryString(b2)+" "+Integer.toBinaryString(b3));
		
		// le faire bit � bit au lieu de par 8?
		
		tmp = a1 + a2 + a3;
		if(tmp > 1) {// on choisi 1
			if(a1==1)
				b1 = lfsr19.generateNextNumber();
			if(a2==1)
				b2 = lfsr22.generateNextNumber();
			if(a3==1)
				b3 = lfsr23.generateNextNumber();
		}
		else {
			if(a1==0)
				b1 = lfsr19.generateNextNumber();
			if(a2==0)
				b2 = lfsr22.generateNextNumber();
			if(a3==0)
				b3 = lfsr23.generateNextNumber();
		}
		
		for(int j=getKEY_LENGTH();--j>=0;) {
			tmp = readBit(b1, j) ^ readBit(b2, j) ^ readBit(b3, j);
			//b = b<<1;
			//b = b|tmp;
			
			//b = (b<<1)|tmp;
			b = addBitLE(b, tmp);
			
			//System.out.println(Integer.toBinaryString(b));
		}
		//System.out.println(Integer.toBinaryString(b));
		
		return b;
	}
	
	public int generateNextNumber(int length) {
		int key = 0;
		int a1, a2, a3;
		int tmp, len = length;

		if(len > 32 || len < 0)	return -1;
		
		for(int k = length-1; k >= 0; k--){
			a1 = readBit(lfsr19.getRegistre(), c1);
			a2 = readBit(lfsr22.getRegistre(), c2);
			a3 = readBit(lfsr23.getRegistre(), c3);
			
			tmp = a1 + a2 + a3;
			//System.out.println("tmp:"+tmp+" /\\a1:"+a1+" & a2:"+a2+" & a3:"+a3);
			if(tmp > 1) {// on choisi 1
				if(a1==1)
					tmp = lfsr19.generateNextNumber(1);
				if(a2==1)
					tmp = lfsr22.generateNextNumber(1);
				if(a3==1)
					tmp = lfsr23.generateNextNumber(1);
			}
			else {
				if(a1==0)
					tmp = lfsr19.generateNextNumber(1);
				if(a2==0)
					tmp = lfsr22.generateNextNumber(1);
				if(a3==0)
					tmp = lfsr23.generateNextNumber(1);
			}
			
			tmp = readBit(lfsr19.getRegistre(), (lfsr19.getSize()-1))
					^ readBit(lfsr22.getRegistre(), (lfsr22.getSize()-1))
					^ readBit(lfsr23.getRegistre(), (lfsr23.getSize()-1));
			//System.out.println("result:"+tmp);
			switch(tmp){
				case 0: key = write0(key,k); break;
				case 1: key = write1(key,k); break;
			}
		}
		//System.out.println("key:"+key);
		return key;
	}
	
	public Lfsr getLfsr19() {	return lfsr19;	}
	public Lfsr getLfsr22() {	return lfsr22;	}
	public Lfsr getLfsr23() {	return lfsr23;	}
	
	public String toString(){
		String str = "A5/1:\n";
		str += "Lfsr 19:\n";
		str += lfsr19.toString();
		str += "Lfsr 22:\n";
		str += lfsr22.toString();
		str += "Lfsr 23:\n";
		str += lfsr23.toString();
		
		return str;
	}
	

	public static void main(String[] args) {
		//A51 a = new A51(0b10101101, 0b10101010, 0b11010111); //registres
		//A51 a = new A51(876, 68735, 4353);
		
		A51 a = new A51("1110101010010010011011000000111010011010001110011111100001011000",
				"1010100011000111010101");
		
		System.out.println("TAGADAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
		System.out.println(a.toString());
		int key = a.generateNextNumber(32);
		System.out.println("Key: "+Integer.toBinaryString(key));
		//System.out.println(a.toString());
		key = a.generateNextNumber(32);
		System.out.println("Key: "+Integer.toBinaryString(key));
		//System.out.println(a.toString());
		key = a.generateNextNumber(32);
		System.out.println("Key: "+Integer.toBinaryString(key));
		//System.out.println(a.toString());
		key = a.generateNextNumber(32);
		System.out.println("Key: "+Integer.toBinaryString(key));
		
		ArrayList<String> init = ((Algorithm)a).generateNewKey(15);
		System.out.println("Init_Key: "+init.get(0)+"\nCounter: "+init.get(1)/*+"\ntmp: "+init.get(2)*/);
	}

}
